﻿using JT808Gateway.Data;
using JT808Gateway.Data.Entity;
using Quartz;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace JT808Gateway.Core.Forwarders
{
    /// <summary>
    /// 转发配置定时刷新
    /// </summary>
    internal class RefreshBindingsJob : IJob
    {
        private List<ForwardingServerBinding> bindings = null;

        private ForwardingServerBindingRepository repository = null;

        public static Dictionary<string, SocketAsyncEventArgs> TcpSessions { get; set; }

        public static Dictionary<string, SocketAsyncEventArgs> UdpSessions { get; set; }

        public RefreshBindingsJob()
        {
            repository = new ForwardingServerBindingRepository();

            TcpSessions = new Dictionary<string, SocketAsyncEventArgs>();
            UdpSessions = new Dictionary<string, SocketAsyncEventArgs>();
        }

        /// <summary>
        /// 定时刷新绑定配置，并且更新通信连接
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Execute(IJobExecutionContext context)
        {
            bindings = await repository.QueryAll();

            foreach (var binding in bindings)
            {
                try
                {
                    if (binding.Enabled)
                    {
                        SocketAsyncEventArgs socketEventArgs = null;

                        if (!TcpSessions.ContainsKey(binding.SimNumber) && binding.ForwardingServer.NetType == EnumNetType.TCP)
                        {
                            socketEventArgs = new SocketAsyncEventArgs();
                            socketEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(binding.ForwardingServer.IPAddress), binding.ForwardingServer.Port);

                            Socket clientSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                            socketEventArgs.UserToken = clientSocket;
                            clientSocket.ConnectAsync(socketEventArgs);

                            TcpSessions.Add(binding.SimNumber, socketEventArgs);
                        }
                        else if (!UdpSessions.ContainsKey(binding.SimNumber) && binding.ForwardingServer.NetType == EnumNetType.UDP)
                        {
                            socketEventArgs = new SocketAsyncEventArgs();
                            socketEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(binding.ForwardingServer.IPAddress), binding.ForwardingServer.Port);

                            Socket clientSocket = new Socket(SocketType.Dgram, ProtocolType.Udp);
                            socketEventArgs.UserToken = clientSocket;
                            clientSocket.ConnectAsync(socketEventArgs);

                            UdpSessions.Add(binding.SimNumber, socketEventArgs);
                        }
                    }
                    else
                    {
                        if (TcpSessions.ContainsKey(binding.SimNumber) && binding.ForwardingServer.NetType == EnumNetType.TCP)
                        {
                            TcpSessions.Remove(binding.SimNumber);
                        }
                        else if (UdpSessions.ContainsKey(binding.SimNumber) && binding.ForwardingServer.NetType == EnumNetType.UDP)
                        {
                            UdpSessions.Remove(binding.SimNumber);
                        }
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }
    }

    /// <summary>
    /// Tcp定时重连
    /// </summary>
    internal class TcpReconnectJob : IJob
    {
        private object _lock = new object();

        public Task Execute(IJobExecutionContext context)
        {
            if (RefreshBindingsJob.TcpSessions.Count > 0)
            {
                lock (_lock)
                {
                    foreach (var session in RefreshBindingsJob.TcpSessions)
                    {
                        var sae = session.Value;
                        try
                        {
                            var socket = (Socket)sae.UserToken;
                            if (socket != null && !socket.Connected)
                            {
                                socket.Close();
                                sae.UserToken = null;

                                Socket newSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                                sae.UserToken = newSocket;

                                newSocket.ConnectAsync(sae);
                            }
                        }
                        catch (Exception ex)
                        {
                        }
                    }
                }
            }
            return Task.CompletedTask;
        }
    }
}